home *** CD-ROM | disk | FTP | other *** search
- /*----------------------------------------------------------------------------
-
- teutil.c
-
- This reusable module contains miscellaneous TextEdit utility routines.
-
- Copyright © 1994-1995, Northwestern University.
-
- ----------------------------------------------------------------------------*/
-
- #include <ctype.h>
-
- #include "def.h"
- #include "teutil.h"
- #include "memutil.h"
- #include "drawutil.h"
- #include "tescroll.h"
-
-
-
- /*----------------------------------------------------------------------------
- HaveTEOutlineHiliteFeature
-
- Determine whether or not this system supports the outline highlighting
- feature.
-
- Exit: function result = true if outline highlighting supported.
- ----------------------------------------------------------------------------*/
-
- Boolean HaveTEOutlineHiliteFeature (void)
- {
- OSErr err = noErr;
- long result;
-
- err = Gestalt(gestaltTextEditVersion, &result);
- return err == noErr && result >= gestaltTE4;
- }
-
-
-
- /*----------------------------------------------------------------------------
- HaveTEGetHiliteRgn
-
- Determine whether or not this system supports the GetTEHiliteRgn
- function.
-
- Exit: function result = true if GetTEHiliteRgn function available.
- ----------------------------------------------------------------------------*/
-
- Boolean HaveTEGetHiliteRgn (void)
- {
- OSErr err = noErr;
- long result;
-
- err = Gestalt(gestaltTEAttr, &result);
- return err == noErr && (result >> gestaltTEHasGetHiliteRgn) & 1 != 0;
- }
-
-
-
- /*----------------------------------------------------------------------------
- MyTESetText
-
- Set the text in a TextEdit record, with memory preflighting.
-
- Entry: textPtr = pointer to text.
- length = length of text.
- theTE = handle to TextEdit record.
-
- Exit: function result = error code.
- ----------------------------------------------------------------------------*/
-
- OSErr MyTESetText (Ptr textPtr, long length, TEHandle theTE)
- {
- long extraMem;
-
- extraMem = length - (**theTE).teLength;
- if (extraMem > 0 && !MemoryAvailable(extraMem + 2000)) return memFullErr;
- TESetText(textPtr, length, theTE);
- return noErr;
- }
-
-
-
- /*----------------------------------------------------------------------------
- MyTEInsert
-
- Insert text in a TextEdit record, with memory preflighting.
-
- Entry: textPtr = pointer to text.
- length = length of text.
- theTE = handle to TextEdit record.
-
- Exit: function result = error code.
- ----------------------------------------------------------------------------*/
-
- OSErr MyTEInsert (Ptr textPtr, long length, TEHandle theTE)
- {
- long extraMem;
-
- extraMem = (**theTE).selEnd - (**theTE).selStart + length;
- if (extraMem > 0 && !MemoryAvailable(extraMem + 2000)) return memFullErr;
- TEInsert(textPtr, length, theTE);
- return noErr;
- }
-
-
-
- /*----------------------------------------------------------------------------
- MyTEGetScrapLen
-
- Get the text scrap length.
-
- Exit: function result = scrap length.
- = 0 if no text in scrap.
- = 0x8000 if text scrap too big.
- ----------------------------------------------------------------------------*/
-
- long MyTEGetScrapLen (void)
- {
- long len, offset;
- OSErr err = noErr;
-
- len = GetScrap(nil, 'TEXT', &offset);
- if (len < 0) return 0;
- if (len > 0x7fff) return 0x8000;
- err = TEFromScrap();
- if (err == teScrapSizeErr) return 0x8000;
- if (err != noErr) return 0;
- return len;
- }
-
-
-
- /*----------------------------------------------------------------------------
- CharIsAWord
-
- Determine whether or not a single character is a "word" (a letter or
- digit).
-
- Entry: str = pointer to string.
- offset = offset in string of character.
-
- Exit: function result = true if character is a "word".
- ----------------------------------------------------------------------------*/
-
- static Boolean CharIsAWord (Ptr str, short offset)
- {
- short x, charType, charClass;
-
- x = CharType(str, offset);
- charType = x & smcTypeMask;
- charClass = x & smcClassMask;
- if (charType == smCharAscii || charType == smCharExtAscii) return true;
- if (charType == smCharPunct && charClass == smPunctNumber) return true;
- return false;
- }
-
-
-
- /*----------------------------------------------------------------------------
- IsWordStart
-
- Determine whether or not a given offset in a string of text is the
- beginning of a word.
-
- Entry: str = pointer to string.
- len = length of string.
- offset = offset in string.
-
- Exit: function result = true if offset is at beginning of a word.
- ----------------------------------------------------------------------------*/
-
- static Boolean IsWordStart (Ptr str, short len, short offset)
- {
- OffsetTable offsets;
- short offFirst, offSecond;
-
- FindWord(str, len, offset, true, nil, offsets);
- offFirst = offsets[0].offFirst;
- offSecond = offsets[0].offSecond;
- if (offFirst != offset) return false;
- if (offSecond > offFirst + 1) return true;
- return CharIsAWord(str, offFirst);
- }
-
-
-
- /*----------------------------------------------------------------------------
- IsWordEnd
-
- Determine whether or not a given offset in a string of text is the
- end of a word.
-
- Entry: str = pointer to string.
- len = length of string.
- offset = offset in string.
-
- Exit: function result = true if offset is at end of a word.
- ----------------------------------------------------------------------------*/
-
- static Boolean IsWordEnd (Ptr str, short len, short offset)
- {
- OffsetTable offsets;
- short offFirst, offSecond;
-
- FindWord(str, len, offset, false, nil, offsets);
- offFirst = offsets[0].offFirst;
- offSecond = offsets[0].offSecond;
- if (offset != offSecond) return false;
- if (offSecond > offFirst + 1) return true;
- return CharIsAWord(str, offFirst);
- }
-
-
-
- /*----------------------------------------------------------------------------
- MyTEDelete
-
- Delete text intelligently.
-
- Entry: theTE = handle to TextEdit record.
- cut = true to cut text to clipboard, false to just delete it.
-
- Exit: *extraSpaceDeleted = true if an extra space character was deleted
- before or after the selected text.
-
- Adapted from Apple's "DragText" sample code.
- ----------------------------------------------------------------------------*/
-
- void MyTEDelete (TEHandle theTE, Boolean cut, Boolean *extraSpaceDeleted)
- {
- short selStart, selEnd, teLength;
- Handle hText;
- char state;
- Boolean selStartsWithWord, selEndsWithWord;
-
- selStart = (**theTE).selStart;
- selEnd = (**theTE).selEnd;
- if (selStart >= selEnd) return;
- teLength = (**theTE).teLength;
- hText = (**theTE).hText;
-
- state = MyHGetState(hText);
- HLock(hText);
- selStartsWithWord = IsWordStart(*hText, teLength, selStart);
- selEndsWithWord = IsWordEnd(*hText, teLength, selEnd);
- MyHSetState(hText, state);
-
- if (cut) {
- TECut(theTE);
- ZeroScrap();
- TEToScrap();
- } else {
- TEDelete(theTE);
- }
-
- *extraSpaceDeleted = false;
- if (selStartsWithWord && selEndsWithWord) {
- if (selStart > 0 && (*hText)[selStart-1] == ' ') {
- TESetSelect(selStart-1, selStart, theTE);
- TEDelete(theTE);
- *extraSpaceDeleted = true;
- } else if (selEnd < teLength && (*hText)[selStart] == ' ') {
- TESetSelect(selStart, selStart+1, theTE);
- TEDelete(theTE);
- *extraSpaceDeleted = true;
- }
- }
- }
-
-
-
- /*----------------------------------------------------------------------------
- MyTECut
-
- Cut text intelligently.
-
- Entry: theTE = handle to TextEdit record.
-
- Adapted from Apple's "DragText" sample code.
- ----------------------------------------------------------------------------*/
-
- void MyTECut (TEHandle theTE)
- {
- Boolean extraSpaceDeleted;
-
- MyTEDelete(theTE, true, &extraSpaceDeleted);
- }
-
-
-
- /*----------------------------------------------------------------------------
- MyTECopy
-
- Copy text.
-
- Entry: theTE = handle to TextEdit record.
- ----------------------------------------------------------------------------*/
-
- void MyTECopy (TEHandle theTE)
- {
- TECopy(theTE);
- ZeroScrap();
- TEToScrap();
- }
-
-
-
- /*----------------------------------------------------------------------------
- MyTEPaste
-
- Paste text intelligently.
-
- Entry: text = pointer to text to be pasted, or nil if the
- text to be pasted is in the TextEdit scrap.
- len = length of text to be pasted.
- theTE = handle to TextEdit record.
- maxLen = maximum legal length for this TextEdit field.
-
- Exit: *extraSpaceAddedInFront = true if extra space character added
- in front of pasted text.
-
- Adapted from Apple's "DragText" sample code.
- ----------------------------------------------------------------------------*/
-
- void MyTEPaste (Ptr text, short len, TEHandle theTE, short maxLen,
- Boolean *extraSpaceAddedInFront)
- {
- Handle h;
- char state;
- Boolean newTextStartsWithWord, newTextEndsWithWord;
- short selStart, selEnd, teLength;
- Handle hText;
- Boolean wordPreceedsSel, wordFollowsSel;
- Boolean roomForExtraSpace;
-
- *extraSpaceAddedInFront = false;
-
- if (text == nil) len = MyTEGetScrapLen();
- if (len == 0) return;
-
- if (text == nil) {
- h = TEScrapHandle();
- state = MyHGetState(h);
- HLock(h);
- newTextStartsWithWord = IsWordStart(*h, len, 0);
- newTextEndsWithWord = IsWordEnd(*h, len, len);
- MyHSetState(h, state);
- } else {
- newTextStartsWithWord = IsWordStart(text, len, 0);
- newTextEndsWithWord = IsWordEnd(text, len, len);
- }
-
- if (newTextStartsWithWord && newTextEndsWithWord) {
-
- selStart = (**theTE).selStart;
- selEnd = (**theTE).selEnd;
- teLength = (**theTE).teLength;
- hText = (**theTE).hText;
-
- state = MyHGetState(hText);
- HLock(hText);
- wordPreceedsSel = selStart > 0 && IsWordEnd(*hText, teLength, selStart);
- wordFollowsSel = selEnd < teLength && IsWordStart(*hText, teLength, selEnd);
- MyHSetState(hText, state);
-
- roomForExtraSpace = teLength - (selEnd - selStart) + len < maxLen;
-
- if (roomForExtraSpace) {
- if (wordPreceedsSel) {
- TEKey(' ', theTE);
- *extraSpaceAddedInFront = true;
- } else if (wordFollowsSel) {
- TEKey(' ', theTE);
- TESetSelect(selStart, selStart, theTE);
- }
- }
-
- }
-
- if (text == nil) {
- TEPaste(theTE);
- } else {
- TEDelete(theTE);
- TEInsert(text, len, theTE);
- }
- }
-
-
-
- /*----------------------------------------------------------------------------
- PtInTEHiliteRgn
-
- Determine whether or not a point is in the current TextEdit hilite
- region.
-
- Entry: where = point in local coords.
- theTE = handle to TextEdit record.
-
- Exit: function result = true if point is in the hilite region.
- ----------------------------------------------------------------------------*/
-
- Boolean PtInTEHiliteRgn (Point where, TEHandle theTE)
- {
- Boolean result = false;
- RgnHandle rgn = nil;
- OSErr err = noErr;
-
- if (!HaveTEGetHiliteRgn()) return false;
- rgn = NewRgn();
- err = TEGetHiliteRgn(rgn, theTE);
- if (err != noErr) goto exit;
- result = PtInRgn(where, rgn);
-
- exit:
-
- if (rgn != nil) DisposeRgn(rgn);
- return result;
- }
-
-
-
- /*----------------------------------------------------------------------------
- SubtractTEHiliteRgn
-
- Subtract a TE hilite region from another region.
-
- Entry: rgn = region handle, in global coordinates.
- theTE = handle to TextEdit record.
- ----------------------------------------------------------------------------*/
-
- void SubtractTEHiliteRgn (RgnHandle rgn, TEHandle theTE)
- {
- RgnHandle hiliteRgn = nil;
- OSErr err = noErr;
-
- if (!HaveTEGetHiliteRgn()) return;
- hiliteRgn = NewRgn();
- err = TEGetHiliteRgn(hiliteRgn, theTE);
- if (err != noErr) goto exit;
- LocalToGlobalRgn(hiliteRgn);
- DiffRgn(rgn, hiliteRgn, rgn);
-
- exit:
-
- if (hiliteRgn != nil) DisposeRgn(hiliteRgn);
- }
-
-
-
- /*----------------------------------------------------------------------------
- DrawTECaret
-
- Draw a caret at a given offset into a TextEdit field.
-
- Entry: offset = offset into field.
- theTE = handle to TextEdit record.
-
- If a caret is already drawn at this location, it is erased.
-
- Adapted from Apple's "DragText" sample code.
- ----------------------------------------------------------------------------*/
-
- void DrawTECaret (short offset, TEHandle theTE)
- {
- short lineHeight, teLength;
- Handle hText;
- Point where;
- short pnMode;
-
- lineHeight = (**theTE).lineHeight;
- teLength = (**theTE).teLength;
- hText = (**theTE).hText;
- where = TEGetPoint(offset, theTE);
- if (offset == teLength && teLength > 0 && (*hText)[teLength - 1] == CR)
- where.v += lineHeight;
- pnMode = qd.thePort->pnMode;
- PenMode(patXor);
- MoveTo(where.h - 1, where.v - 1);
- Line(0, 1 - lineHeight);
- PenMode(pnMode);
- }
-
-
-
- /*----------------------------------------------------------------------------
- TEIsFrontOfLine
-
- Determine whether an offset in a TextEdit field is at the beginning of a
- line.
-
- Entry: offset = offset into field.
- theTE = handle to TextEdit record.
-
- Exit: function result = true if offset is at beginning of line.
-
- Adapted from Apple's "DragText" sample code.
- ----------------------------------------------------------------------------*/
-
- Boolean TEIsFrontOfLine (short offset, TEHandle theTE)
- {
- short line = 0, teLength;
- Handle hText;
- short *lineStarts;
-
- teLength = (**theTE).teLength;
- hText = (**theTE).hText;
- lineStarts = (**theTE).lineStarts;
-
- if (teLength == 0) return true;
-
- if (offset >= teLength)
- return (*hText)[teLength - 1] == CR;
-
- while (lineStarts[line] < offset) line++;
-
- return lineStarts[line] == offset;
- }
-
-
-
- /*----------------------------------------------------------------------------
- MyTEGetOffset
-
- Get the offset in the text corresponding to a point in a TextEdit field.
-
- Entry: where = point in local coords.
- theTE = handle to TextEdit record.
-
- Exit: function result = offset into text.
-
- Adapted from Apple's "DragText" sample code.
- ----------------------------------------------------------------------------*/
-
- short MyTEGetOffset (Point where, TEHandle theTE)
- {
- short offset;
- Handle hText;
- char prevChar;
- Point pt;
-
- offset = TEGetOffset(where, theTE);
- hText = (**theTE).hText;
- if (!TEIsFrontOfLine(offset, theTE)) return offset;
- if (offset == 0) return offset;
- prevChar = (*hText)[offset-1];
- if (prevChar == CR) return offset;
- if (!isLWSP(prevChar)) return offset;
- pt = TEGetPoint(offset-1, theTE);
- if (pt.h >= where.h) return offset;
- return offset - 1;
- }
-
-
-
- /*----------------------------------------------------------------------------
- MyTEActivate
-
- Activate a TextEdit field, but only if the window containing the field
- is active.
-
- Entry: theTE = handle to TextEdit record.
- ----------------------------------------------------------------------------*/
-
- void MyTEActivate (TEHandle theTE)
- {
- WindowPeek windPeek;
-
- windPeek = (WindowPeek)(**theTE).inPort;
- if (windPeek->hilited) TEActivate(theTE);
- }
-
-
-
- /*----------------------------------------------------------------------------
- GetBol
-
- Get the beginning of the line containing a character.
-
- Entry: offset = offset into text of character.
- clikStuff = 0 if offset on line boundary is at end of
- previous line, non-zero if offset on line boundary
- is at beginning of next line.
- theTE = handle to TextEdit record.
-
- Exit: function result = offset into text of beginning of line
- containing the character.
- ----------------------------------------------------------------------------*/
-
- static short GetBol (short offset, short clikStuff, TEHandle theTE)
- {
- short *lineStarts;
- short teLength;
-
- teLength = (**theTE).teLength;
- lineStarts = (**theTE).lineStarts;
- if (offset <= 0) {
- return 0;
- } else if (offset >= teLength) {
- offset = teLength;
- }
- while (*lineStarts < offset) lineStarts++;
- if (*lineStarts == offset && clikStuff != 0) {
- return *lineStarts;
- } else {
- return *(lineStarts-1);
- }
- }
-
-
-
- /*----------------------------------------------------------------------------
- GetEol
-
- Get the end of the line containing a character.
-
- Entry: offset = offset into text of character.
- clikStuff = 0 if offset on line boundary is at end of
- previous line, non-zero if offset on line boundary
- is at beginning of next line.
- theTE = handle to TextEdit record.
-
- Exit: function result = offset into text of end of line
- containing the character.
- ----------------------------------------------------------------------------*/
-
- static short GetEol (short offset, short clikStuff, TEHandle theTE)
- {
- short *lineStarts;
- short teLength;
-
- teLength = (**theTE).teLength;
- lineStarts = (**theTE).lineStarts;
- if (offset <= 0) {
- offset = 0;
- clikStuff = 0xffff;
- } else if (offset >= teLength) {
- return teLength;
- }
- while (*lineStarts < offset) lineStarts++;
- if (*lineStarts == offset && clikStuff != 0) {
- return *(lineStarts+1);
- } else {
- return *lineStarts;
- }
- }
-
-
-
- /*----------------------------------------------------------------------------
- GetTEPageHeight
-
- Get the page height for a TextEdit field - the number of lines in
- the view rectangle minus 1.
-
- Entry: theTE = handle to TextEdit record.
-
- Exit: function result = page height
- ----------------------------------------------------------------------------*/
-
- short GetTEPageHeight (TEHandle theTE)
- {
- short lineHeight;
- Rect viewRect;
-
- lineHeight = (**theTE).lineHeight;
- viewRect = (**theTE).viewRect;
- return (viewRect.bottom - viewRect.top) / lineHeight - 1;
- }
-
-
-
- /*----------------------------------------------------------------------------
- GetBop
-
- Get the beginning of the page.
-
- Entry: offset = offset into text of character.
- theTE = handle to TextEdit record.
- pageHeight = number of lines in a page, or 0 to
- compute this based on the assumption that the
- view rectangle of the TextEdit field is a page.
-
- Exit: function result = offset into text of beginning of page
- containing the character, or beginning of previous
- page if already at top of page.
- ----------------------------------------------------------------------------*/
-
- static short GetBop (short offset, TEHandle theTE, short pageHeight)
- {
- Rect viewRect;
- Point where;
- short bop;
- short *lineStarts, *p;
-
- viewRect = (**theTE).viewRect;
- where.h = viewRect.left;
- where.v = viewRect.top + 2;
- bop = MyTEGetOffset(where, theTE);
- if (bop < offset) return bop;
- if (pageHeight == 0) pageHeight = GetTEPageHeight(theTE);
- lineStarts = (**theTE).lineStarts;
- p = lineStarts;
- while (*p < bop) p++;
- p -= pageHeight;
- if (p < lineStarts) p = lineStarts;
- return *p;
- }
-
-
-
- /*----------------------------------------------------------------------------
- GetEop
-
- Get the end of the page.
-
- Entry: offset = offset into text of character.
- theTE = handle to TextEdit record.
- pageHeight = number of lines in a page, or 0 to
- compute this based on the assumption that the
- view rectangle of the TextEdit field is a page.
-
- Exit: function result = offset into text of end of page
- containing the character, or end of next
- page if already at end of page.
- ----------------------------------------------------------------------------*/
-
- static short GetEop (short offset, TEHandle theTE, short pageHeight)
- {
- Rect viewRect;
- Point where;
- short eop;
- short *lineStarts, *p, *lineStartsEnd;
- short teLength;
- Handle hText;
-
- viewRect = (**theTE).viewRect;
- where.h = viewRect.right - 2;
- where.v = viewRect.bottom - 2;
- eop = MyTEGetOffset(where, theTE);
- teLength = (**theTE).teLength;
- hText = (**theTE).hText;
- if (eop < teLength && *(*hText + eop) == CR) eop++;
- if (offset < teLength && *(*hText + offset) == CR) offset++;
- if (eop > offset) return eop;
- if (pageHeight == 0) pageHeight = GetTEPageHeight(theTE);
- lineStarts = (**theTE).lineStarts;
- lineStartsEnd = lineStarts + (**theTE).nLines;
- p = lineStarts;
- while (*p < eop) p++;
- p += pageHeight;
- if (p > lineStartsEnd) p = lineStartsEnd;
- return *p;
- }
-
-
-
- /*----------------------------------------------------------------------------
- GetPixelsFromBolGivenOffset
-
- Get the the number of pixels from the beginning of a line to a given
- character in the line.
-
- Entry: bol = offset into text of beginning of line containing the
- character.
- offset = offset into text of character.
- theTE = handle to TextEdit record.
-
- Exit: function result = number of pixels from the beginning of the
- line to the character.
- ----------------------------------------------------------------------------*/
-
- static short GetPixelsFromBolGivenOffset (short bol, short offset, TEHandle theTE)
- {
- Handle hText;
- char state;
- short result;
-
- hText = (**theTE).hText;
- state = MyHGetState(hText);
- HLock(hText);
- result = TextWidth(*hText, bol, offset-bol);
- MyHSetState(hText, state);
- return result;
- }
-
-
-
- /*----------------------------------------------------------------------------
- GetOffsetGivenPixelsFromBol
-
- Get the offset of the character which is a given number of pixels into
- a line.
-
- Entry: bol = offset into text of beginning of line.
- pixelsFromBol = number of pixels.
- theTE = handle to TextEdit record.
-
- Exit: function result = offset into text of the character which is
- the given number of pixels into the line.
- ----------------------------------------------------------------------------*/
-
- static short GetOffsetGivenPixelsFromBol (short bol, short pixelsFromBol, TEHandle theTE)
- {
- Handle hText;
- char state;
- short result;
- short eol;
-
- hText = (**theTE).hText;
- state = MyHGetState(hText);
- HLock(hText);
-
- eol = GetEol(bol, 0xffff, theTE);
- result = bol;
- while (result < eol && TextWidth(*hText, bol, result - bol) < pixelsFromBol) result++;
-
- MyHSetState(hText, state);
- return result;
- }
-
-
-
- /*----------------------------------------------------------------------------
- MyGetClikStuff
-
- Get the clikStuff field from an edit record, adjusted if the insertion
- point follows a final CR in the field.
-
- Entry: theTE = handle to TextEdit record.
-
- Exit: function result = adjusted clikStuff field.
- ----------------------------------------------------------------------------*/
-
- short MyGetClikStuff (TEHandle theTE)
- {
- short selStart, selEnd, clikStuff, teLength;
- Handle hText;
-
- selStart = (**theTE).selStart;
- selEnd = (**theTE).selEnd;
- clikStuff = (**theTE).clikStuff;
- hText = (**theTE).hText;
- teLength = (**theTE).teLength;
- if (selStart == selEnd && selStart == teLength) {
- if (clikStuff == 0 && selStart > 0 && *(*hText + selStart - 1) == CR) {
- (**theTE).clikStuff = clikStuff = 0xffff;
- } else if (clikStuff != 0 && (selStart == 0 || *(*hText + selStart - 1) != CR)) {
- (**theTE).clikStuff = clikStuff = 0;
- }
- }
- return clikStuff;
- }
-
-
-
- /*----------------------------------------------------------------------------
- MyGetTEHiliteOrCaretRgn
-
- Get the current hilite region for a TextEdit field if there is
- a selection range, or a region containing the caret if there
- is no selection range.
-
- Entry: rgn = handle to region.
- theTE = handle to TextEdit record.
-
- Exit: rgn modified to contain hilite or caret region.
- ----------------------------------------------------------------------------*/
-
- static void MyGetTEHiliteOrCaretRgn (RgnHandle rgn, TEHandle theTE)
- {
- short selStart, selEnd, lineHeight;
- Point where;
-
- selStart = (**theTE).selStart;
- selEnd = (**theTE).selEnd;
- lineHeight = (**theTE).lineHeight;
- if (selStart == selEnd) {
- where = TEGetPoint(selStart, theTE);
- SetRectRgn(rgn, where.h - 1, where.v - lineHeight, where.h + 1, where.v);
- } else {
- TEGetHiliteRgn(rgn, theTE);
- }
- }
-
-
-
- /*----------------------------------------------------------------------------
- TEArrowKey
-
- Handle arrow keys in TextEdit windows.
-
- Entry: theChar = leftArrow, rightArrow, upArrow, or downArrow.
- modifiers = modifiers field from event record.
- theTE = handle to TextEdit record.
- pageHeight = number of lines in a page, or 0 to have TEArrowKey
- compute this itself based on the assumption that the
- view rectangle of the TextEdit field is a page.
- prevEvent = pointer to previous event.
-
- Exit: selection range adjusted in the TextEdit record.
- *scrollIntoView = offset into TextEdit record which
- should be scrolled into view.
-
- Warning: The function proves that if you hack long enough, you can make
- TextEdit do anything you want it to do, but only at the risk of tying
- your brain into complete knots. This code is incredibly complicated and
- sensitive.
- ----------------------------------------------------------------------------*/
-
- void TEArrowKey (char theChar, short modifiers, TEHandle theTE,
- short pageHeight, EventRecord *prevEvent, short *scrollIntoView)
- {
- short selStart, selEnd, teLength, offset, bol, eol;
- Boolean shift, option, command;
- Boolean prevWasArrowKey;
- char prevChar;
- short prevModifiers;
- Boolean isUpDown;
- Boolean shiftArrowSequence, upDownArrowSequence;
- Handle hText;
- Boolean haveTEOutlineHilite;
- short savedOutlineHilite;
- OffsetTable wordOffsets;
- char state;
- char c;
- short newSelStart, newSelEnd, oldOffset;
- static RgnHandle selRgnOld = nil, selRgnNew = nil;
- static short anchor = 0;
- static short upDownPixelsFromBol = 0;
- static short clikStuff = 0xffff;
-
- selStart = (**theTE).selStart;
- selEnd = (**theTE).selEnd;
- teLength = (**theTE).teLength;
- hText = (**theTE).hText;
- shift = (modifiers & shiftKey) != 0;
- option = (modifiers & optionKey) != 0;
- command = (modifiers & cmdKey) != 0;
- isUpDown = theChar == upArrow || theChar == downArrow;
- prevChar = prevEvent->message & 0xff;
- prevModifiers = prevEvent->modifiers;
- prevWasArrowKey = (prevEvent->what == keyDown || prevEvent->what == autoKey) &&
- IsArrowKey(prevChar);
- shiftArrowSequence = shift && prevWasArrowKey &&
- (prevModifiers & shiftKey) != 0;
- upDownArrowSequence = isUpDown && prevWasArrowKey && !option && !command &&
- (prevChar == upArrow || prevChar == downArrow) &&
- (prevModifiers & optionKey) == 0 &&
- (prevModifiers & cmdKey) == 0;
-
- if (shift && !shiftArrowSequence)
- anchor = theChar == upArrow || theChar == leftArrow ?
- selEnd : selStart;
-
- if (shiftArrowSequence) {
- offset = anchor == selStart ? selEnd : selStart;
- } else {
- offset = theChar == upArrow || theChar == leftArrow ? selStart : selEnd;
- }
-
- oldOffset = offset;
-
- if (!option && !command) {
-
- if (isUpDown && !upDownArrowSequence) {
- clikStuff = selStart == selEnd ? MyGetClikStuff(theTE) : 0xffff;
- bol = GetBol(offset, clikStuff, theTE);
- upDownPixelsFromBol = GetPixelsFromBolGivenOffset(bol, offset, theTE);
- }
-
- switch (theChar) {
- case leftArrow:
- if (shift || selStart == selEnd) offset--;
- clikStuff = 0xffff;
- break;
- case rightArrow:
- if (shift || selStart == selEnd) offset++;
- clikStuff = 0xffff;
- break;
- case upArrow:
- if (shift && upDownPixelsFromBol > 0 &&
- offset < teLength && *(*hText+offset) == CR) offset++;
- if (shift && offset != anchor) {
- clikStuff = upDownPixelsFromBol > 0 ? 0 : 0xffff;
- } else {
- clikStuff = MyGetClikStuff(theTE);
- }
- bol = GetBol(offset, clikStuff, theTE);
- if (bol > 0) {
- bol = GetBol(bol, 0, theTE);
- offset = GetOffsetGivenPixelsFromBol(bol, upDownPixelsFromBol, theTE);
- } else if (offset == oldOffset) {
- offset = 0;
- }
- clikStuff = offset > bol ? 0 : 0xffff;
- if (shift && upDownPixelsFromBol > 0 &&
- offset > 0 && *(*hText+offset-1) == CR) offset--;
- break;
- case downArrow:
- if (shift && upDownPixelsFromBol > 0 &&
- offset < teLength && *(*hText+offset) == CR) offset++;
- if (shift && offset != anchor) {
- clikStuff = upDownPixelsFromBol > 0 ? 0 : 0xffff;
- } else {
- clikStuff = MyGetClikStuff(theTE);
- }
- bol = GetEol(offset, clikStuff, theTE);
- if (bol < teLength || (shift && upDownPixelsFromBol == 0) ||
- (teLength > 0 && *(*hText + teLength - 1) == CR))
- offset = GetOffsetGivenPixelsFromBol(bol, upDownPixelsFromBol, theTE);
- if (bol >= teLength && offset == oldOffset) {
- offset = teLength;
- }
- clikStuff = offset > bol ? 0 : 0xffff;
- if (shift && upDownPixelsFromBol > 0 &&
- offset > 0 && *(*hText+offset-1) == CR) offset--;
- break;
- }
-
- } else if (option && !command) {
-
- switch (theChar) {
- case leftArrow:
- offset--;
- while (offset >= 0) {
- c = *(*hText + offset);
- if (c < 0 || isalnum(c)) break;
- offset--;
- }
- offset++;
- state = MyHGetState(hText);
- HLock(hText);
- FindWord(*hText, teLength, offset, false, nil, wordOffsets);
- MyHSetState(hText, state);
- offset = wordOffsets[0].offFirst;
- clikStuff = 0xffff;
- break;
- case rightArrow:
- while (offset < teLength) {
- c = *(*hText + offset);
- if (c < 0 || isalnum(c)) break;
- offset++;
- }
- state = MyHGetState(hText);
- HLock(hText);
- FindWord(*hText, teLength, offset, true, nil, wordOffsets);
- MyHSetState(hText, state);
- offset = wordOffsets[0].offSecond;
- clikStuff = 0;
- break;
- case upArrow:
- offset--;
- while (offset >= 0 && isLWSPorCR(*(*hText + offset))) offset--;
- while (offset >= 0) {
- while (offset >= 0 && !isLWSPorCR(*(*hText + offset))) offset--;
- if (offset < 0) break;
- offset--;
- while (offset >= 0 && isLWSP(*(*hText + offset))) offset--;
- if (offset < 0 || *(*hText + offset) == CR) break;
- }
- offset++;
- while (offset < teLength && isLWSPorCR(*(*hText + offset))) offset++;
- clikStuff = 0xffff;
- break;
- case downArrow:
- while (offset < teLength && isLWSPorCR(*(*hText + offset))) offset++;
- while (offset < teLength) {
- while (offset < teLength && *(*hText + offset) != CR) offset++;
- if (offset >= teLength) break;
- offset++;
- if (isLWSPorCR(*(*hText + offset))) break;
- }
- if (offset < teLength) {
- offset--;
- while (offset >= 0 && isLWSPorCR(*(*hText + offset))) offset--;
- offset++;
- }
- clikStuff = 0;
- break;
- }
-
- } else if (!option && command) {
-
- switch (theChar) {
- case leftArrow:
- clikStuff = selStart == selEnd ? MyGetClikStuff(theTE) : 0xffff;
- bol = GetBol(offset, clikStuff, theTE);
- if (offset > bol) {
- offset = bol;
- } else {
- offset = GetBol(offset, 0, theTE);
- }
- clikStuff = 0xffff;
- break;
- case rightArrow:
- clikStuff = selStart == selEnd ? MyGetClikStuff(theTE) : 0xffff;
- eol = GetEol(offset, clikStuff, theTE);
- if (offset < eol) {
- if (offset == eol-1 && *(*hText + offset) == CR) {
- offset = GetEol(eol, 0xffff, theTE);
- } else {
- offset = eol;
- }
- } else {
- offset = GetEol(offset, 0xffff, theTE);
- }
- clikStuff = 0;
- break;
- case upArrow:
- offset = GetBop(offset, theTE, pageHeight);
- clikStuff = 0xffff;
- break;
- case downArrow:
- offset = GetEop(offset, theTE, pageHeight);
- clikStuff = 0;
- break;
- }
-
- } else { /* option && command */
-
- switch (theChar) {
- case upArrow:
- offset = 0;
- clikStuff = 0xffff;
- break;
- case downArrow:
- offset = teLength;
- clikStuff = 0;
- break;
- }
-
- }
-
- if ((!shift || anchor == offset) && clikStuff == 0 &&
- offset > 0 && *(*hText + offset - 1) == CR)
- {
- offset--;
- clikStuff = 0xffff;
- }
-
- if (offset < 0) {
- offset = 0;
- } else if (offset > teLength) {
- offset = teLength;
- }
-
- if (!shift) {
- if (MyGetClikStuff(theTE) != clikStuff && selStart == selEnd) {
- haveTEOutlineHilite = HaveTEOutlineHiliteFeature();
- if (haveTEOutlineHilite)
- savedOutlineHilite = TEFeatureFlag(teFOutlineHilite, TEBitClear, theTE);
- TEDeactivate(theTE);
- (**theTE).clikStuff = clikStuff;
- TESetSelect(offset, offset, theTE);
- TEActivate(theTE);
- if (haveTEOutlineHilite)
- TEFeatureFlag(teFOutlineHilite, savedOutlineHilite, theTE);
- } else {
- (**theTE).clikStuff = clikStuff;
- TESetSelect(offset, offset, theTE);
- }
- } else {
- if (offset <= anchor) {
- newSelStart = offset;
- newSelEnd = anchor;
- } else {
- newSelStart = anchor;
- newSelEnd = offset;
- }
- if (HaveTEGetHiliteRgn()) {
- if (selRgnOld == nil) selRgnOld = NewRgn();
- if (selRgnNew == nil) selRgnNew = NewRgn();
- MyGetTEHiliteOrCaretRgn(selRgnOld, theTE);
- (**theTE).selStart = newSelStart;
- (**theTE).selEnd = newSelEnd;
- MyGetTEHiliteOrCaretRgn(selRgnNew, theTE);
- (**theTE).selStart = selStart;
- (**theTE).selEnd = selEnd;
- if (selStart == selEnd || newSelStart == newSelEnd) {
- UnionRgn(selRgnNew, selRgnOld, selRgnNew);
- } else {
- XorRgn(selRgnNew, selRgnOld, selRgnNew);
- }
- SetClip(selRgnNew);
- }
- TESetSelect(newSelStart, newSelEnd, theTE);
- ClipRect(&qd.thePort->portRect);
- }
-
- *scrollIntoView = offset;
- }
-
-
-
- /*----------------------------------------------------------------------------
- IsArrowKey
-
- Check to see if a character is an arrow key.
-
- Entry: theChar = character.
-
- Exit: function result = true if arrow key.
- ----------------------------------------------------------------------------*/
-
- Boolean IsArrowKey (char theChar)
- {
- return theChar == upArrow || theChar == downArrow ||
- theChar == leftArrow || theChar == rightArrow;
- }
-
-
-
- /*----------------------------------------------------------------------------
- MyTEClick
-
- Handle a click in a textedit field.
-
- Entry: thePt = location of click in local coords.
- extend = true to extend selection (shift key down).
- hTE = handle to textedit record.
-
- This function is identical to the standard TEClick function, except it
- contains a hack to handle a problem with clickloop functions in native
- mode. The hack is similar to the one used in the SetListClickLoop
- function in listutil.c.
-
- The problem is that textedit clickloop functions return their Boolean
- function result using the Z bit in the CC register. This doesn't
- work properly with the Mixed Mode manager. So we interpolate some
- 68K glue code to always return with the Z bit clear, indicating a
- fucntion result of true. All the clickloop functions in NewsWatcher
- return true. If a clickloop function needs to return false, this
- function won't work properly.
-
- Apple has been mucking with the TextEdit.h header file definition for
- uppTEClickLoopProcInfo. In some versions of the universal headers, this
- is defined with the function result returned in D0, and with those
- versions, this hack is not needed. In other versions, it's defined with
- the function result returned in the Z bit, and with those versions, we
- need the hack. For NewsWatcher's purposes, this hack works with both
- versions, so I'm keeping it.
- ----------------------------------------------------------------------------*/
-
- void MyTEClick (Point thePt, Boolean extend, TEHandle hTE)
- {
- #ifdef powerc
-
- #pragma options align=mac68k
- static struct clickLoopGlue {
- long move; /* movea.l clickLoopUPP,a0 */
- short jsr; /* jsr (a0) */
- short mvq; /* moveq #1,d0 - clears CC Z bit to return true */
- short rts; /* rts */
- TEClickLoopUPP clickLoopUPP; /* the UPP */
- } clickLoop68K = {
- 0x207A0008,
- 0x4E90,
- 0x7001,
- 0x4E75,
- 0
- };
- #pragma options align=reset
-
-
- if ((**hTE).clickLoop == nil) {
- TEClick(thePt, extend, hTE);
- } else {
- clickLoop68K.clickLoopUPP = (**hTE).clickLoop;
- (**hTE).clickLoop = (TEClickLoopUPP)&clickLoop68K;
- TEClick(thePt, extend, hTE);
- (**hTE).clickLoop = clickLoop68K.clickLoopUPP;
- }
-
- #else
-
- TEClick(thePt, extend, hTE);
-
- #endif
- }